#include "interrupt.h"
#include "stdint.h"
#include "global.h"
#include "io.h"
#include "print.h"
#include "debug.h"
#define IDT_DESC_CNT 0x22

#define PIC_M_CTRL 0x20	       // 这里用的可编程中断控制器是8259A,主片的控制端口是0x20
#define PIC_M_DATA 0x21	       // 主片的数据端口是0x21
#define PIC_S_CTRL 0xa0	       // 从片的控制端口是0xa0
#define PIC_S_DATA 0xa1	       // 从片的数据端口是0xa1

#define EFLAG_IF 0X00000200  //if 位
#define GET_EFLAG(EFLAG_VAR) asm volatile ( "pushfl;popl %0" : "=g"(EFLAG_VAR));     //获取eflag

extern  interrupt_handler intr_entry_table[IDT_DESC_CNT];

//extern void put_str(uint8_t *c);

//中断处理函数
interrupt_handler idt_handler_table[IDT_DESC_CNT];
char* intr_name[IDT_DESC_CNT];

struct intr_desc{
    uint16_t intr_addr_low;
    uint16_t intr_selector;
    uint8_t  unuse;
    uint8_t  attribute ; //type,S,DPL,P 
    uint16_t int_addr_high;
};

static struct intr_desc idt_table[IDT_DESC_CNT];

static void make_idt_desc(struct intr_desc* idt , uint8_t attr, interrupt_handler func);

static void pic_init(void);

static void idt_desc_init(void);

static void load_idt(void);

static void exception_init(void);

void register_handler(uint32_t intrruptNo,interrupt_handler handler)
{
    ASSERT( intrruptNo < IDT_DESC_CNT);
    idt_handler_table[intrruptNo] = handler;
}

void idt_init(void)
{
    exception_init();
    pic_init();
    idt_desc_init();
    load_idt();
    put_str("idt init done!\n");

   
}

enum intr_status intr_enable(void)
{
    enum intr_status origin_status = get_intr_status();
    if( origin_status == INTR_OFF){
        asm volatile("sti");
    }
    return origin_status;
    
}

enum intr_status intr_disable(void)
{
    enum intr_status origin_status = get_intr_status();
    if( origin_status == INTR_ON){
        asm volatile("cli":::"memory");
    }
    return origin_status;
    
}
enum intr_status set_intr_status(enum intr_status intrStatus)
{
    return (intrStatus & INTR_ON)? intr_enable() : intr_disable();
}


enum intr_status get_intr_status(void)
{
    uint32_t eflag;
    GET_EFLAG(eflag);
    return (eflag & EFLAG_IF)? INTR_ON : INTR_OFF;
}

void common_handler(uint32_t intrruptNo)
{
    intr_disable();
    set_cursor_pos(0);
    put_str("============================================\r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("                                            \r");
    put_str("============================================\r");
    set_cursor_pos(0);
    put_str("\r");put_str("Exception! Exception No. :");put_int(intrruptNo);put_str("\r");
    put_str(intr_name[intrruptNo]);put_str("\r");
    PANIC("ERROR!!!\n");
}


static void exception_init()
{
    int i = 0;
    for(i = 0 ;i < IDT_DESC_CNT;i++){
        idt_handler_table[i] = common_handler;
    }
   
    intr_name[0] = "#DE Divide Error";
    intr_name[1] = "#DB Debug Exception";
    intr_name[2] = "NMI Interrupt";
    intr_name[3] = "#BP Breakpoint Exception";
    intr_name[4] = "#OF Overflow Exception";
    intr_name[5] = "#BR BOUND Range Exceeded Exception";
    intr_name[6] = "#UD Invalid Opcode Exception";
    intr_name[7] = "#NM Device Not Available Exception";
    intr_name[8] = "#DF Double Fault Exception";
    intr_name[9] = "Coprocessor Segment Overrun";
    intr_name[10] = "#TS Invalid TSS Exception";
    intr_name[11] = "#NP Segment Not Present";
    intr_name[12] = "#SS Stack Fault Exception";
    intr_name[13] = "#GP General Protection Exception";
    intr_name[14] = "#PF Page-Fault Exception";
    // intr_name[15] 第15项是intel保留项，未使用
    intr_name[16] = "#MF x87 FPU Floating-Point Error";
    intr_name[17] = "#AC Alignment Check Exception";
    intr_name[18] = "#MC Machine-Check Exception";
    intr_name[19] = "#XF SIMD Floating-Point Exception";
    
}

static void load_idt()
{
    uint64_t idtr = ((uint64_t)(uint32_t)idt_table << 16) | (sizeof(idt_table) - 1);
    asm volatile ( "lidt %0" : : "m" (idtr));    
}

//以下基本都是固定写法。不需要理解
static void pic_init(void)
{
    /* 初始化主片 */
   outb (PIC_M_CTRL, 0x11);   // ICW1: 边沿触发,级联8259, 需要ICW4.
   outb (PIC_M_DATA, 0x20);   // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27.
   outb (PIC_M_DATA, 0x04);   // ICW3: IR2接从片. 
   outb (PIC_M_DATA, 0x01);   // ICW4: 8086模式, 正常EOI

   /* 初始化从片 */
   outb (PIC_S_CTRL, 0x11);	// ICW1: 边沿触发,级联8259, 需要ICW4.
   outb (PIC_S_DATA, 0x28);	// ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F.
   outb (PIC_S_DATA, 0x02);	// ICW3: 设置从片连接到主片的IR2引脚
   outb (PIC_S_DATA, 0x01);	// ICW4: 8086模式, 正常EOI

    // 只开放时钟中断
    outb(PIC_M_DATA,0b11111100); //  1111 1110 ,时钟中断在主片的 IRQ0位置上
    outb(PIC_S_DATA,0b11111111); // 关闭从片所有中断

    put_str("pic init done!\n");
}

static void idt_desc_init(void)
{
    int i;
    for(i = 0 ; i < IDT_DESC_CNT; i ++){
        make_idt_desc(&(idt_table[i]), IDT_DESC_ATTR_DPL0, intr_entry_table[i]);
    }
    put_str("idt desc init done!\n");
}

static void make_idt_desc(struct intr_desc* idt , uint8_t attr, interrupt_handler func)
{
    idt->intr_addr_low = (uint32_t) func & 0x0000FFFF;
    idt->intr_selector = SELECTOR_K_CODE;
    idt->unuse = 0;
    idt->attribute = attr;
    idt->int_addr_high = ((uint32_t)func & 0xffff0000) >> 16;

}